﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Configuration;
using System.Windows.Forms;

namespace ACE
{
    public class Controller
    {
        #region declare
        private static string RestUrl = "controller/rest";
        private static string ApplicationsUrl = "applications";
        private static string BusinessTransactionsUrl = "business-transactions";
        private static string TiersUrl = "tiers";
        //private static string BackendsUrl = "backends"; //NIY
        private static string NodesUrl = "nodes";
        //private static string EventsUrl = "events"; //NIY
        private HttpClient httpClient;

        //public string ApplicationName { get; internal set; }
        public bool Authenticated { get; internal set; }
        //public string TierName { get; internal set; }
        //public string NodeName { get; internal set; }

        public string ControllerUrl { get; internal set; }
        public string Account { get; internal set; }
        public string UserName { get; internal set; }
        public string Password { get; internal set; }
        public int Index { get; internal set; }
        public bool PortalLogin { get; internal set; }
        #endregion

        //public HttpClient ADHttpClient
        //{
        //    get { return httpClient; }
        //    set { httpClient = value; }
        //}
        public Controller()
        {
        }

        public Controller(int index, string controllerUrl, string account, string userName, string password, string portalLogin)
        {
            Index = index;
            ControllerUrl = controllerUrl;
            Account = account;
            UserName = userName;
            Password = password;
            PortalLogin = string.IsNullOrEmpty(portalLogin) || portalLogin == "0" ? false : true;
        }

        public bool AuthenticatePortal(string controllerUrl, string account, string userName, string password, string url)
        {
            httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri(controllerUrl);
            httpClient.DefaultRequestHeaders.Accept.Clear();
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

            //string credentials = string.Format("{0}:{1}", userName, password);

            //byte[] cred = UTF8Encoding.UTF8.GetBytes(credentials);
            //httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(cred));

            HttpResponseMessage message;
            message = httpClient.GetAsync(url).Result;

            if (message.IsSuccessStatusCode)
            {
                string result = message.Content.ReadAsStringAsync().Result;
                Console.WriteLine(result);
            }
            else
            {
                Authenticated = false;
                throw new Exception(string.Format("Error authenticating. URL: {0}. Reason phrase: {1}. Request message:{2}.", RestUrl, message.ReasonPhrase, message.RequestMessage));
            }
            Authenticated = true;
            return Authenticated;
        }

        public bool Authenticate()
        {
            return Authenticate(ControllerUrl, Account, UserName, Password, ApplicationsUrl, PortalLogin);
        }

        public bool Authenticate(string controllerUrl, string account, string userName, string password, string url, bool portalLogin)
        {
            httpClient = new HttpClient();
            httpClient.BaseAddress = new Uri(controllerUrl);
            httpClient.DefaultRequestHeaders.Accept.Clear();
            httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/xml"));

            if (!portalLogin)
            {
                //joe.doe@myaccount:password
                string credentials = string.Format("{0}@{1}:{2}", userName, account, password);

                byte[] cred = UTF8Encoding.UTF8.GetBytes(credentials);
                httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(cred));
            }
            HttpResponseMessage message;

            message = this.GetAsync(url);

            if (message.IsSuccessStatusCode)
            {
                string result = message.Content.ReadAsStringAsync().Result;
            }
            else
            {
                Authenticated = false;
                throw new Exception(string.Format("Error authenticating. URL: {0}. Reason phrase: {1}. Request message:{2}.", RestUrl, message.ReasonPhrase, message.RequestMessage));
            }
            Authenticated = true;
            return Authenticated;
        }

        private HttpResponseMessage GetAsync(string url)
        {
            string fullUrl = string.Empty;
            if(url != string.Empty)
            {
                fullUrl = string.Format("{0}/{1}", RestUrl, url);
            }
            else
            {
                //This should never happen and can be removed if not
                fullUrl = RestUrl;
                throw new Exception("url is empty!");
            }
            return httpClient.GetAsync(fullUrl).Result;
        }

        //        private static void Authenticate()
        //215         { 
        //216             if (string.IsNullOrEmpty(Credentials)) 
        //217             { 
        //218                 throw new Exception("Credentials were not supplied."); 
        //219             } 
        //220             var url = string.Format("{0}/{1}", LoadUrl, "authentication-point/authenticate"); 
        //221             byte[] cred = UTF8Encoding.UTF8.GetBytes(Credentials); 
        //222             HttpClient1.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(cred)); 
        //223             //HTTPCLIENT.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json")); 
        //224              
        //225             HttpResponseMessage message = HttpClient1.GetAsync(url).Result; 
        //226 

        //227             string description = string.Empty; 
        //228             if (message.IsSuccessStatusCode) 
        //229             { 
        //230                 string result = message.Content.ReadAsStringAsync().Result; 
        //231                 description = result; 
        //232             } 
        //233             else 
        //234             { 
        //235                 description = message.ReasonPhrase; 
        //236                 throw new Exception(string.Format("Error authenticating. URL: {0}. Reason phrase: {1}. Request message:{2}.", url, message.ReasonPhrase, message.RequestMessage)); 
        //237             } 
        //238         } 

        public List<ADApplication> GetApplications(string applicationName)
        {
            if(!Authenticated)
            {
                try
                {
                    Authenticate();
                }
                catch(Exception e)
                {
                    MessageBox.Show(e.Message, "Authentication Error");
                }
            }
            var message = GetAsync(ApplicationsUrl);

            if (message.IsSuccessStatusCode)
            {
                string result = null;
                result = message.Content.ReadAsStringAsync().Result;
                var applications = new List<ADApplication>();

                var xmlDocument = new System.Xml.XmlDocument();
                xmlDocument.LoadXml(result);
                XmlElement root = xmlDocument.DocumentElement;

                foreach (XmlNode a in root)
                {
                    string id = a["id"].InnerText;
                    string name = a["name"].InnerText;
                    string description = a["description"] != null ? a["description"].InnerText : "";
                    if (string.IsNullOrEmpty(applicationName) || name.ToLower() == applicationName.ToLower())
                    {
                        applications.Add(new ADApplication(id, name, description));
                    }

                }

                return applications;
            }
            else
            {
                throw new Exception(string.Format("Error authenticating. URL: {0}. Reason phrase: {1}. Request message:{2}.", RestUrl, message.ReasonPhrase, message.RequestMessage));
            }
        }
        public List<Tier> GetTiers(string applicationName, string tierName)
        {
            var message = GetAsync(string.Format("{0}/{1}/{2}", ApplicationsUrl, applicationName.Replace("/", "//"), TiersUrl));

            if (message.IsSuccessStatusCode)
            {
                string result = null;
                result = message.Content.ReadAsStringAsync().Result;
                var tiers = new List<Tier>();

                var xmlDocument = new System.Xml.XmlDocument();
                xmlDocument.LoadXml(result);
                XmlElement root = xmlDocument.DocumentElement;

                foreach (XmlNode a in root)
                {
                    string id = a["id"].InnerText;
                    string name = a["name"].InnerText;
                    string type = a["type"].InnerText;
                    string agentType = a["agentType"] != null ? a["agentType"].InnerText : "";
                    string numberOfNodes = a["numberOfNodes"].InnerText;
                    if (string.IsNullOrEmpty(tierName) || tierName.ToLower() == name.ToLower())
                    {
                        tiers.Add(new Tier(id, name, type, agentType, numberOfNodes));
                    }
                }

                return tiers;
            }
            else
            {
                throw new Exception(string.Format("Error authenticating. URL: {0}. Reason phrase: {1}. Request message:{2}.", RestUrl, message.ReasonPhrase, message.RequestMessage));
            }
        }

        public List<Node> GetNodes(string applicationName, string tierNameParam, string nodeName, bool unique)
        {
            //Invalid URI: Encoded slashes are not allowed by default.  To enable encodedslashes, set the property com.sun.grizzly.util.buf.UDecoder.ALLOW_ENCODED_SLASH to true.
            var fullUrl = string.IsNullOrEmpty(tierNameParam) ?
                    string.Format("{0}/{1}/{2}", ApplicationsUrl, applicationName, NodesUrl) :
                    string.Format("{0}/{1}/{2}/{3}/{4}", ApplicationsUrl, applicationName, TiersUrl, tierNameParam, NodesUrl);

            var nodes = new List<Node>();

            var message = GetAsync(fullUrl);

            if (message.IsSuccessStatusCode)
            {
                string result = null;
                result = message.Content.ReadAsStringAsync().Result;

                var xmlDocument = new XmlDocument();
                xmlDocument.LoadXml(result);
                XmlElement root = xmlDocument.DocumentElement;

                foreach (XmlNode a in root)
                {
                    string id = a["id"].InnerText;
                    string name = a["name"].InnerText;
                    string type = a["type"].InnerText;
                    string tierId = a["tierId"] != null ? a["tierId"].InnerText : "";
                    string tierName = a["tierName"].InnerText;
                    string machineId = a["machineId"].InnerText;
                    string machineName = a["machineName"].InnerText;
                    string machineOSType = a["machineOSType"].InnerText;
                    string machineAgentPresent = a["machineAgentPresent"].InnerText;
                    string machineAgentVersion = a["machineAgentVersion"] != null ? a["machineAgentVersion"].InnerText: "";
                    string appAgentPresent = a["appAgentPresent"].InnerText;
                    string appAgentVersion = a["appAgentVersion"] != null ? a["appAgentVersion"].InnerText : "";
                    string ipAddresses = a["ipAddresses"] != null ? a["ipAddresses"].InnerText : "";
                    string ipAddress = "";// a["ipAddress"].InnerText;
                    string agentType = a["agentType"] != null ? a["agentType"].InnerText : "";
                    if (string.IsNullOrEmpty(nodeName) || machineName.ToLower() == nodeName.ToLower())
                    {
                        if (!unique || !MachineNameExists(nodes, machineName))
                        {
                            nodes.Add(new Node(id, name, type, tierId, tierName, machineId, machineName, machineOSType, machineAgentPresent, machineAgentVersion, appAgentPresent, appAgentVersion, ipAddress, agentType));
                        }
                    }
                }

            }
            else
            {
                //throw new Exception(string.Format("Error authenticating. URL: {0}. Reason phrase: {1}. Request message:{2}.", RestUrl, message.ReasonPhrase, message.RequestMessage));
            }
            return nodes;
        }

        private bool MachineNameExists(List<Node> nodes, string machineName)
        {
            foreach(Node n in nodes)
            {
                if(n.MachineName.ToLower() == machineName.ToLower())
                {
                    return true;
                }
            }
            return false;
        }

        public List<BT> GetBTs(string appName, string tierNameParam, string btName)
        {

            var fullUrl = string.IsNullOrEmpty(tierNameParam) ?
                string.Format("{0}/{1}/{2}", ApplicationsUrl, appName.Replace("/", "//"), BusinessTransactionsUrl) :
                string.Format("{0}/{1}/{2}/{3}/{4}", ApplicationsUrl, appName.Replace("/", "//"), TiersUrl, tierNameParam.Replace("/", "%2F"), BusinessTransactionsUrl);

            fullUrl = string.Format("{0}/{1}/{2}", ApplicationsUrl, appName.Replace("/", "//"), BusinessTransactionsUrl);

            var message = GetAsync(fullUrl);
            const string defaultAdTxName = "_APPDYNAMICS_DEFAULT_TX_";
            if (message.IsSuccessStatusCode)
            {
                string result = null;
                result = message.Content.ReadAsStringAsync().Result;
                var bts = new List<BT>();

                var xmlDocument = new System.Xml.XmlDocument();
                xmlDocument.LoadXml(result);
                XmlElement root = xmlDocument.DocumentElement;

                foreach (XmlNode a in root)
                {
                    string id = a["id"].InnerText;
                    string name = a["name"].InnerText;
                    string entryPointType = a["entryPointType"].InnerText;
                    string internalName = a["internalName"].InnerText;
                    string tierId = a["internalName"] != null ? a["tierId"].InnerText : "";
                    string tierName = a["tierName"].InnerText;
                    string background = a["background"].InnerText;
                    
                    var matchesTier = false;
                    if (string.IsNullOrEmpty(tierNameParam) || string.Equals(tierName, tierNameParam, StringComparison.OrdinalIgnoreCase))
                    {
                        matchesTier = true;
                    }
                    
                    var matchesBTName = false;
                    if (string.IsNullOrEmpty(btName) || string.Equals(name, btName, StringComparison.OrdinalIgnoreCase))
                    {
                        matchesBTName = true;
                    }
                    if (matchesTier && matchesBTName)
                    {
                        if (!string.Equals(defaultAdTxName, name, StringComparison.InvariantCulture))
                        {
                            bts.Add(new BT(id, name, entryPointType, internalName, tierId, tierName, background));
                        }
                    }
                }

                return bts;
            }
            else
            {
                throw new Exception(string.Format("Error authenticating. URL: {0}. Reason phrase: {1}. Request message:{2}.", RestUrl, message.ReasonPhrase, message.RequestMessage));
            }
        }


        public List<Metric> GetMetrics(string applicationName, string tierName, string btName, string metricCategoryInputParam, string metricNameInputParam, string durationInMinutes)
        {
            //No BT
            //applications/eBiz Production CHDC/metric-data?metric-path=Overall Application Performance|Calls per Minute&time-range-type=BEFORE_NOW&duration-in-mins=1440&rollup=0

            //Business Transaction Performance|Business Transactions|DotCom|DotCOM_HTML_Corporate_Shop_ProductOverview|Calls per Minute
            //https://comcast-xfinity.saas.appdynamics.com/controller/rest/applications/eBiz%20Production%20WCDC/metric-data?metric-path=Business%20Transaction%20Performance%7CBusiness%20Transactions%7CDotCom%7CDotCOM_HTML_Corporate_Shop_ProductOverview%7CCalls%20per%20Minute&time-range-type=BEFORE_NOW&duration-in-mins=15

            const string MetricPathUrl = "metric-data";
            const string MetricParam = "metric-path";
            const string TimeRangeTypeParam = "time-range-type";
            const string TimeRangeTypeBeforeNow = "BEFORE_NOW";
            //const string TimeRangeTypeBetweenTimes = "BETWEEN_TIMES";
            //const string StartTimeParam = "";
            //const string EndTimeParam = "";
            const string DurationInMinutesParam = "duration-in-mins";
            const string RollupParam = "rollup";
            const string BusinessTransactionPerformance = "Business Transaction Performance";
            const string BusinessTransactions = "Business Transactions";

            string rollupValue = "0";
            var fullUrl = string.Empty;

            fullUrl = string.Format(
                "{0}/{1}/{2}?{3}={4}|{5}&{6}={7}&{8}={9}&{10}={11}",
                ApplicationsUrl, applicationName,
                MetricPathUrl, MetricParam,
                metricCategoryInputParam, metricNameInputParam,
                TimeRangeTypeParam, TimeRangeTypeBeforeNow,
                DurationInMinutesParam, durationInMinutes,
                RollupParam, rollupValue
                );

            if (!string.IsNullOrEmpty(tierName) && !string.IsNullOrEmpty(btName))
            {
                fullUrl = string.Format(
                    "{0}/{1}/{2}?{3}={4}|{5}|{6}|{7}|{8}&{9}={10}&{11}={12}&{13}={14}",
                    ApplicationsUrl, applicationName,
                    MetricPathUrl, MetricParam,
                    BusinessTransactionPerformance, BusinessTransactions, tierName, btName,
                    metricNameInputParam,
                    TimeRangeTypeParam, TimeRangeTypeBeforeNow,
                    DurationInMinutesParam, durationInMinutes,
                    RollupParam, rollupValue
                    );
            }

            fullUrl = Uri.EscapeUriString(fullUrl);

            var message = GetAsync(fullUrl);

            if (message.IsSuccessStatusCode)
            {
                string result = null;
                result = message.Content.ReadAsStringAsync().Result;
                var metricData = new List<Metric>();

                var xmlDocument = new System.Xml.XmlDocument();
                xmlDocument.LoadXml(result);
                XmlElement root = xmlDocument.DocumentElement;

                foreach (XmlNode m in root)
                {
                    string metricId = m["metricId"].InnerText;
                    string metricPath = m["metricPath"].InnerText;
                    string metricName = m["metricName"].InnerText;
                    string frequency = m["frequency"].InnerText;

                    XmlNodeList metrics = m["metricValues"].ChildNodes;
                    var metricValues = new List<MetricValue>();

                    foreach (XmlNode metric in metrics)
                    {
                        string startTimeInMillis = metric["startTimeInMillis"].InnerText;
                        string value = metric["value"].InnerText;
                        string min = metric["min"].InnerText;
                        string max = metric["max"].InnerText;
                        string current = metric["current"].InnerText;
                        string sum = metric["sum"].InnerText;
                        string count = metric["count"].InnerText;
                        string standardDeviation = metric["standardDeviation"].InnerText;
                        string occurrences = metric["occurrences"].InnerText;
                        string useRange = metric["useRange"].InnerText;
                        metricValues.Add(new MetricValue(startTimeInMillis, value, min, max, current, sum, count, standardDeviation, occurrences, useRange));
                    }
                    metricData.Add(new Metric(metricId, metricPath, metricName, frequency, metricValues));
                }

                return metricData;
            }
            else
            {
                throw new Exception(string.Format("Error authenticating. URL: {0}. Reason phrase: {1}. Request message:{2}.", RestUrl, message.ReasonPhrase, message.RequestMessage));
            }
        }

    }

}
